<?php

/**
 * 插件处理类
 *
 * @author edikud
 * @package Plugin
 * @copyright Copyright (c) 2019 Cola (http://www.mcooo.com)
 * @license GNU General Public License 2.0
 */
class Plugins
{
    /**
     * 所有启用的插件
     *
     * @access private
     * @var array
     */
    private static $_plugins = array();
	
    public static function refresh()
    {
		Opcache::clean();
        File::rmdir(__COLA_DIR__ . '/var/runtime/');
    }
	
    /**
     * 插件初始化
     *
     * @access public
     * @param array $plugins 插件列表
     * @return void
     */
    public static function init(array $plugins)
    {
        self::$_plugins = $plugins;
    }

    /**
     * 启用插件
     *
     * @access public
     * @param string $pluginName 插件名称
     * @return void
     */
    public static function activate($pluginName)
    {
        self::$_plugins[$pluginName] = $pluginName;
    }
    /**
     * 禁用插件
     *
     * @access public
     * @param string $pluginName 插件名称
     * @return void
     */
    public static function deactivate($pluginName)
    {
		self::$_plugins[$pluginName] = NULL;
		unset(self::$_plugins[$pluginName]);
    }

    public static function parseHook($string)
    {
		$plugin_enable = array_keys(self::$_plugins);
		if(FALSE === strpos($string, "//HOOK")) return $string;
		$new_arr = array();
		$arr = explode("//HOOK", $string);
		$i = 0;
        foreach ($arr as $part) 
		{
            if($i%2 == 1) 
			{
				$pos = strpos($part, ';');
				$hook = trim(substr($part, 0, $pos));
				$plugged = '';
				$prefix_left = NULL;
				$seg = '=';
				$prefix_right = NULL;
				$pass = false;
				$tmp = '';
				if('{' == $hook[0])
				{
				    list($plugged, $tmp) = explode('}', $hook);
					$plugged = trim(substr($plugged, 1));
					$tmp  = trim($tmp);
				}else{
					$tmp  = trim($hook);
				}
				
				if('$' == $tmp[0])
				{
					list($prefix, $tmp2) = explode('|', $tmp);
					$tmp2 = trim($tmp2);
					$prefix = trim($prefix);
					$list = explode(' ', $prefix);
					isset($list[0]) && $prefix_left = trim($list[0]);
					isset($list[1]) && $seg = trim($list[1]);
					isset($list[2]) && $prefix_right = trim($list[2]);

				} else {
					$tmp2 = trim($tmp);
				}
				list($method)  = explode('(', $tmp2);
		        
		        foreach($plugin_enable as $plugin_name) 
				{
					if(class_exists('Plugin_' . $plugin_name) && method_exists('Plugin_' . $plugin_name, $method))
					{ 
					    NULL !== $prefix_left && $new_arr[] = $prefix_left . ' ' . $seg . ' ';
					    $new_arr[] = 'Plugin_' . $plugin_name . '::' . $tmp2 . ';';
					    $pass = true;
					}
		        }
				if(!$pass && NULL !== $prefix_right)
				{
					$new_arr[] = $prefix_left . ' ' . $seg . ' ' . $prefix_right . ';';
				}
				$plugged && $new_arr[] = $plugged . ($pass ? ' = true;' : ' = false;');
				$new_arr[] = substr($part, $pos + 1);
				
			} else {
				
				++$i;
				$new_arr[] = $part;
			}	
        }
		return implode('', $new_arr);
    }
	
	

    /**
     * 导出当前插件设置
     *
     * @access public
     * @return array
     */
    public static function export()
    {
        return self::$_plugins;
    }

    /**
     * 版本依赖性检测
     *
     * @access public
     * @param string $version 程序版本
     * @param string $versionRange 依赖的版本规则
     * @return boolean
     */
    public static function checkDependence($version, $versionRange)
    {
        //如果没有检测规则,直接掠过
        if (empty($versionRange)) {
            return true;
        }

        $items = array_map('trim', explode('-', $versionRange));
        if (count($items) < 2) {
            $items[1] = '9999.9999.9999';
        }

        list ($minVersion, $maxVersion) = $items;

        //对*和?的支持,4个9是最大版本
        $minVersion = str_replace(array('*', '?'), array('9999', '9'), $minVersion);
        $maxVersion = str_replace(array('*', '?'), array('9999', '9'), $maxVersion);

        if (version_compare($version, $minVersion, '>=') && version_compare($version, $maxVersion, '<=')) {
            return true;
        }

        return false;
    }

    /**
     * 判断插件是否存在
     *
     * @access public
     * @param string $pluginName 插件名称
     * @return mixed
     */
    public static function exists($pluginName) 
	{
        return isset(self::$_plugins[$pluginName]);
    }
	
    /**
     * 获取插件文件的头信息
     *
     * @access public
     * @param string $pluginFile 插件文件路径
     * @return array
     */
    public static function parseInfo($pluginFile)
    {
        $tokens = token_get_all(file_get_contents($pluginFile));
        $isDoc = false;
        $isFunction = false;
        $isClass = false;
        $isInClass = false;
        $isInFunction = false;
        $isDefined = false;
        $current = NULL;

        /** 初始信息 */
        $info = array(
            'description'       => '',
            'title'             => '',
            'author'            => '',
            'homepage'          => '',
            'version'           => '',
            'dependence'        => '',
            'activate'          => false,
            'deactivate'        => false,
            'config'            => false,
        );

        $map = array(
            'package'   =>  'title',
            'author'    =>  'author',
            'link'      =>  'homepage',
            'dependence'=>  'dependence',
            'version'   =>  'version'
        );

        foreach ($tokens as $token) {
            /** 获取doc comment */
            if (!$isDoc && is_array($token) && T_DOC_COMMENT == $token[0]) {

                /** 分行读取 */
                $described = false;
                $lines = preg_split("(\r|\n)", $token[1]);
                foreach ($lines as $line) {
                    $line = trim($line);
                    if (!empty($line) && '*' == $line[0]) {
                        $line = trim(substr($line, 1));
                        if (!$described && !empty($line) && '@' == $line[0]) {
                            $described = true;
                        }

                        if (!$described && !empty($line)) {
                            $info['description'] .= $line . "\n";
                        } else if ($described && !empty($line) && '@' == $line[0]) {
                            $info['description'] = trim($info['description']);
                            $line = trim(substr($line, 1));
                            $args = explode(' ', $line);
                            $key = array_shift($args);

                            if (isset($map[$key])) {
                                $info[$map[$key]] = trim(implode(' ', $args));
                            }
                        }
                    }
                }
                $isDoc = true;
            }

            if (is_array($token)) {
                switch ($token[0]) {
                    case T_FUNCTION:
                        $isFunction = true;
                        break;
                    case T_IMPLEMENTS:
                        $isClass = true;
                        break;
                    case T_WHITESPACE:
                    case T_COMMENT:
                    case T_DOC_COMMENT:
                        break;
                    case T_STRING:
                        $string = strtolower($token[1]);
                        switch ($string) {
                            case 'plugins_interface':
                                $isInClass = $isClass;
                                break;
                            case 'activate':
                            case 'deactivate':
                            case 'config':
                                if ($isFunction) {
                                    $current = $string;
                                }
                                break;
                            default:
                                if (!empty($current) && $isInFunction && $isInClass) {
                                    $info[$current] = true;
                                }
                                break;
                        }
                        break;
                    default:
                        if (!empty($current) && $isInFunction && $isInClass) {
                            $info[$current] = true;
                        }
                        break;
                }
            } else {
                $token = strtolower($token);
                switch ($token) {
                    case '{':
                        if ($isDefined) {
                            $isInFunction = true;
                        }
                        break;
                    case '(':
                        if ($isFunction && !$isDefined) {
                            $isDefined = true;
                        }
                        break;
                    case '}':
                    case ';':
                        $isDefined = false;
                        $isFunction = false;
                        $isInFunction = false;
                        $current = NULL;
                        break;
                    default:
                        if (!empty($current) && $isInFunction && $isInClass) {
                            $info[$current] = true;
                        }
                        break;
                }
            }
        }

        return $info;
    }
}
